﻿using System;
using System.Linq;
using StreetPacMan.Server.Facade;
using StreetPacMan.Server.Rules.PlayerLocationChangeRules;

namespace StreetPacMan.Server.Rules
{
    public class ApplesEatingRule : IHandlePlayerLocationChangedRule
    {
        private readonly IApplesEatingRule[] _AppleEatingRules;
        public ApplesEatingRule()
        {
            _AppleEatingRules = new IApplesEatingRule[]
                                    {
                                        new RemoveEatenApplesFromBoardRule(),
                                        new AddScoreForEatenApplesRule(),
                                        new ChangeGameStateAndMessageIfEatenPowerPelletRule(),
                                        new GameOverPacMenWinRule(),
                                        new AddEventForApplesEatingRule(),
                                    };
        }

        public void Handle(PacManDataContext dataContext, ServerPollParameters serverPollParameters, ref bool shouldContinueHandling)
        {
            var updateEntity = dataContext.Players.Where(x => x.Id == serverPollParameters.EntityId).Single();
            if (updateEntity.RoleEnum != PlayerRole.PacMan)
            {
                return;
            }

            Apple[] allApplesEatenByPacMan = GetAllApplesEatenByPacMan(updateEntity);
            foreach (var applesEatingRule in _AppleEatingRules)
            {
                applesEatingRule.DoSideEffects(allApplesEatenByPacMan, updateEntity);
            }

            dataContext.SubmitChanges();
        }

        private Apple[] GetAllApplesEatenByPacMan(Player pacMan)
        {
            return pacMan
                .Game
                .Apples
                .Where(x => x.State == (int)AppleState.Active)
                .ToArray() //required here since the linq2sql query cannot take the method call to the db
                .Where(x => TwoGeos.GetDistance(pacMan.LastGeoLat, pacMan.LastGeoLon, x.GeoLat, x.GeoLon) < GameDistances.SameLocationMaxRange)
                .ToArray();
        }

        public void EatAllApplesInPath(Player player, TwoGeos path)
        {
            Apple[] allApplesEatenByPacMan = GetAllApplesInPath(player.Game, path);
            foreach (var applesEatingRule in _AppleEatingRules)
            {
                applesEatingRule.DoSideEffects(allApplesEatenByPacMan, player);
            }
        }

        private Apple[] GetAllApplesInPath(Game game, TwoGeos path)
        {
            var borderWidth = TwoGeos.OneMeterEstimate*GameDistances.ApplesDistancesSpreadout;
            var lowLat = Math.Min(path.First.Lat, path.Second.Lat) - borderWidth;
            var highLat = Math.Max(path.First.Lat, path.Second.Lat) + borderWidth;
            var lowLon = Math.Min(path.First.Lon, path.Second.Lon) - borderWidth;
            var highLon = Math.Max(path.First.Lon, path.Second.Lon) + borderWidth;

            var liveApplesInRect = game.Apples
                               .Where(x => x.State == (int)AppleState.Active)
                               .Where(x => x.GeoLat <= highLat && x.GeoLat >= lowLat &&
                                                   x.GeoLon <= highLon && x.GeoLon >= lowLon)
                .ToArray(); //required here since the linq2sql query cannot take the method call to the db

            var pointsInPacManPath =
                path
                .GetPointsOnVectorEveryXMeters(GameDistances.ApplesDistancesSpreadout)
                .ToArray();

            return 
                liveApplesInRect
                    .Where(a => pointsInPacManPath.Any(p =>
                            TwoGeos.GetDistance(a.GeoLat, a.GeoLon, p.Lat, p.Lon) < GameDistances.SameLocationMaxRange))
                    .ToArray();
        }
    }
}